home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-05
/
drivers1.zip
/
ISOLINK.ASM
< prev
next >
Wrap
Assembly Source File
|
1992-01-23
|
54KB
|
1,633 lines
page 54,132
; Packet driver for BICC Data Networks' ISOLINK 4110-2 ethernet
; controller, written by
; Rainer Toebbicke
; European Organisation of Nuclear Research (CERN)
; Geneva, Switzerland
; based on the "generic" packet driver by Russell Nelson.
version equ 2 ;this is the minor version
include defs.asm ;SEE ENCLOSED COPYRIGHT MESSAGE
; BICC ISOLINK card constants
.286c ; we are at least on a 80286!
code segment para public
assume cs:code, ds:code
public begin ;makes us appear in the link map
begin equ $ ;used for alignment operations below
;Lance initialisation block, must be on word boundary
;
; Initialization Block Mode operation Bit Definitions.
;
M_PROM equ 8000h ; Promiscuous Mode
M_INTL equ 0040h ; Internal Loopback
M_DRTY equ 0020h ; Disable Retry
M_COLL equ 0010h ; Force Collision
M_DTCR equ 0008h ; Disable Transmit CRC)
M_LOOP equ 0004h ; Loopback
M_DTX equ 0002h ; Disable the Transmitter
M_DRX equ 0001h ; Disable the Reciever
IBmode dw 00h ;mode word
IBpadr db 6 dup (0) ;physical addr
IBladrf db 8 dup (0ffh) ;logical addr filter
IBrdraL dw 0 ;Receive Descr Ring ptr
IBrdraH dw 0
IBrdraHF equ byte ptr IBrdraH+1
IBtdraL dw 0 ;Transmit Descr Ring ptr
IBtdraH dw 0
IBtdraHF equ byte ptr IBtdraH+1
CSR0 equ 0
c0_ERR equ 8000h
c0_BABL equ 4000h
c0_CERR equ 2000h
c0_MISS equ 1000h
c0_MERR equ 0800h
c0_RINT equ 0400h
c0_TINT equ 0200h
c0_ErrClear equ c0_BABL+c0_CERR+c0_MISS+c0_MERR
c0_IDON equ 0100h
c0_INTR equ 0080h
c0_INEA equ 0040h
c0_RXON equ 0020h
c0_TXON equ 0010h
c0_TDMD equ 0008h
c0_STOP equ 0004h
c0_STRT equ 0002h
c0_INIT equ 0001h
CSR1 equ 1
CSR2 equ 2
CSR3 equ 3
RD struc ;Receive descriptor
RBadrL dw 0
RBadrH dw 0
RBbcnt dw 0 ;buffer size
RBmcnt dw 0 ;packet size
RD ends
RBflags equ byte ptr RBadrH+1
RBown equ 080h ;1=owned by Lance, 0=by host
RBerr equ 040h ;error summary bit
RBfram equ 020h
RBoflo equ 010h
RBcrc equ 008h
RBbuff equ 004h
RBstp equ 002h ;start of packet
RBenp equ 001h ;end of packet
TD struc ;Transmit descriptor
TBadrL dw 0
TBadrH dw 0
TBbcnt dw 0 ;buffer size
TBtdr dw 0 ;more flags
TD ends
TBflags equ byte ptr TBadrH+1
TBown equ 080h ;1=owned by Lance, 0=by host
TBerr equ 040h ;error summary bit
TBstp equ 002h ;Start of packet
TBenp equ 001h ;End of packet
TBbcntF equ byte ptr TBbcnt+1
gary db 0 ; DEBUG
garycount db 0 ; DEBUG
public int_no
int_no dw 10,0 ;must be four bytes long for get_number.
public driver_class, driver_type, driver_name, driver_function, parameter_list
driver_class db BLUEBOOK, IEEE8023, 0 ;from the packet spec
driver_type db 5 ;from the packet spec
driver_name db 'ISOLINK',0 ;name of the driver.
driver_function db 2
parameter_list label byte
db 1 ;major rev of packet driver
db 9 ;minor rev of packet driver
db 14 ;length of parameter list
db EADDR_LEN ;length of MAC-layer address
dw GIANT ;MTU, including MAC headers
dw MAX_MULTICAST * EADDR_LEN ;buffer size of multicast addrs
dw 0 ;(# of back-to-back MTU rcvs) - 1
dw 0 ;(# of successive xmits) - 1
int_num dw 0 ;Interrupt # to hook for post-EOI
;processing, 0 == none,
extrn sys_features: byte
public rcv_modes
rcv_modes dw 7 ;number of receive modes in our table.
dw 0
dw rcv_mode_1
dw 0
dw rcv_mode_3
dw 0,0
dw rcv_mode_6
rbfstart dw FirstDescr ;1st Rcv buffer descr
rbfcurr dw ?
tbfstart dw ? ;1st Xmit buffer descr
tbfcurr dw ?
tbfend dw ? ;end of Xmit buffers
xmt_buffsz_r dw ? ;xmit buffer sizes
xmt_buffsz_rn dw ? ;same, but times -1
pklen dw ? ;length of packet
pklen_rem dw ? ;remaining to be received
rcv_csr0 dw ?
rcvbuffp dd ?
RAP dw 0 ;Register Address Port
RDP dw 0 ;Register Data Port
DMAPrt db 0,0,0,0 ;DMA port number.
options db 0
options_HMA equ 40h ;running in HMA
word_16 dw 16
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Gary Spanswick - 3/5/90
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
CACPCtrl equ 90h ; Address of CACP control register
DMACascade EQU 0D0H ; DMA Operates in Cascaded Mode
DMAUnmask EQU 00H ; DMA Operation is to unmask
DMAC1ModeReg EQU 0BH ; DMA Mode Register Address
DMAC2ModeReg EQU 0D6H ; DMA Mode Register Address
DMAC1MaskReg EQU 0AH ; DMA Mask Register Address
DMAC2MaskReg EQU 0D4H ; DMA Mask Register Address
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
extrn count_out_err:near, count_in_err:near
seg2lin proc near
; convert (ax:dx=offset:segment) to linear address (ax:dx=low:high)
push bx
push ax ;save offset
mov ax,dx
mul word_16 ;segment to linear address
pop bx
add ax,bx ;plus offset
adc dx,0
pop bx ;restore
ret
seg2lin endp
lin2seg proc near
; convert linear addr (ax:dx=low:high) to (ax:dx=offset:segment)
; with minimal offset to avoid wrap-around problems
; the 80286 can address 0-10ffefh in real mode:
cmp dl,010h ;over 1 Megabyte?
je l2sHMA ;yes, segment is 0FFFFh
push ax
shr ax,4 ;convert to paragraphs
shl dx,4+8
or dx,ax ;segment ok
pop ax
and ax,0fh ;offset
ret
l2sHMA:
; effectively subtract 0ffff0h from the linear address to form the
; offset:
mov dx,0ffffh ;this we know already
sub ax,0fff0h ;only have to do low order word
ret
lin2seg endp
; write bx to Lance control & status reg [ax]
wrcsr0 proc near
xor ax,ax ;write to CSR0
wrcsr: ;CSR in ax
mov dx,RAP ;address CSR
out dx,ax
mov dx,RDP ;data port
mov ax,bx
out dx,ax
ret
wrcsr0 endp
; read Lance control reg [ax]
rdcsr0 proc near
xor ax,ax ;read CSR 0
rdcsr: ;CSR in ax
mov dx,RAP
out dx,ax
mov dx,RDP
in ax,dx
ret
rdcsr0 endp
rcv_nxt_d proc near
; release current and advance to next receive descriptor
mov RBflags[bx],RBown ;give buffer to Lance
add bx,8
cmp bx,tbfstart ;end of ring?
jb rcv_nxt_ok ;no...
mov bx,rbfstart ;restart at first descriptor
rcv_nxt_ok:
mov rbfcurr,bx
ret
rcv_nxt_d endp
xmt_nxt_d proc near
xmt_nxt_d endp
wait_own_0 proc near
; wait for "own" bit in descriptor [bx] to clear
test TBflags[bx],TBown
jnz wo_wait ;no, have to wait
ret
wo_wait:
push cx ;save reg
mov cx,0ffffh
wo_lp1:
test TBflags[bx],TBown
jz wo_clear ;ok...
loop wo_lp1
wo_clear:
pop cx
ret
wait_own_0 endp
rcv_getaddr proc near
; obtain buffer addr from descriptor [bx]
mov ax,RBadrL[bx]
mov dx,RBadrH[bx]
call lin2seg
mov word ptr rcvbuffp,ax
mov word ptr rcvbuffp+2,dx
ret
rcv_getaddr endp
public as_send_pkt
; The Asynchronous Transmit Packet routine.
; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
; interrupts possibly enabled.
; Exit with nc if ok, or else cy if error, dh set to error number.
; es:di and interrupt enable flag preserved on exit.
as_send_pkt:
ret
public drop_pkt
; Drop a packet from the queue.
; Enter with es:di -> iocb.
drop_pkt:
assume ds:nothing
ret
public xmit
; Process a transmit interrupt with the least possible latency to achieve
; back-to-back packet transmissions.
; May only use ax and dx.
xmit:
assume ds:nothing
ret
ErrExit proc near
mov ah,9
int 21h
stc
ret
ErrExit endp
LoadInitBlock proc near
; set initialisation block addr
push ds
pop dx
mov ax,offset IBmode
call seg2lin
push dx ;don't loose it
mov bx,ax
mov ax,CSR1
call wrcsr ;low order 16 bits of addr
pop bx
mov ax,CSR2
call wrcsr ;high order bits
ret
LoadInitBlock endp
InitLance proc near
; init the controller and wait for completion
mov bx,c0_INIT
call wrcsr0
mov cx,-1 ;wait for completion
eto_initlp:
call rdcsr0
test ax,c0_IDON ;done?
jnz init_ok ;good!
loop eto_initlp
mov dx,offset init_errmsg
call outofHMA ;get out of HMA
call errexit
init_ok:
ret
InitLance endp
public send_pkt
send_pkt proc near
;enter with ds:si -> packet, cx = packet length.
;exit with nc if ok, or else cy if error, dh set to error number.
assume ds:nothing
push ds ;save packet segment
push cs
pop ds
assume ds:code
; packet must be at least 64 bytes long (with fcs)...
cmp cx, 82 ; DEBUG
jb Fred1 ; DEBUG
mov gary,1 ; DEBUG
Fred1: ; DEBUG
cmp gary,1
jne fred2
inc garycount
fred2:
cmp cx,RUNT
jnl send_L_ok
mov cl,RUNT
send_L_ok:
; get next buffer descriptor
mov bx,tbfcurr
call wait_own_0 ;wait until it's free
; If the next buffer is big enough to hold the packet, we will
; copy the packet and send it out.
; Otherwise we send it from the user's buffer but then have to
; wait until it is sent out.
; This may speed up fast machines on the expense of some memory
cmp cx,xmt_buffsz_r ;longer than our buffers?
jng send_copy ;no, copy the packet
send_user_buff: ;send from user's buffer
mov ax,si ;input buffer addr
pop dx ;segment
call seg2lin ;convert to linear addr
push TBadrL[bx] ;save original buffer address
push TBadrH[bx] ;...
mov TBadrL[bx],ax
mov TBadrH[bx],dx
jmp short send_send
send_copy:
mov ax,TBadrL[bx]
mov dx,TBadrH[bx] ;buffer address
call lin2seg ;convert to offset:segment
mov es,dx
mov di,ax
pop ds ;restore packet segment
assume ds:nothing
push cx ;save length
test cx,1 ;uneven length?
jz send_even
movsb
send_even:
shr cx,1 ;convert to words, can't be zero
rep movsw ;copy buffer
pop cx
push cs
pop ds ;restore ds
assume ds:code
send_send:
neg cx ;length in two's complement
mov TBbcnt[bx],cx
or TBflags[bx],TBown+TBstp+TBenp
push bx ;save ring entry address
mov bx,c0_TDMD+c0_INEA
call wrcsr0 ;start transmitter immediately
pop bx ;restore ring entry addr
xor al,al ;assume no error
cmp cx,xmt_buffsz_rn ;was this a big buffer?
jnl send_next ;no, all done
call wait_own_0 ;wait for send ok
mov al,TBflags[bx] ;save flags
mov cx,xmt_buffsz_rn ;original buffer size negated
pop TBadrH[bx]
pop TBadrL[bx]
mov TBbcnt[bx],cx
; advance to next transmit descriptor
send_next:
add bx,8
cmp bx,tbfend ;end of ring?
jb xmt_nxt_ok ;no...
mov bx,tbfstart ;restart at first descriptor
xmt_nxt_ok:
mov tbfcurr,bx
send_done:
test al,TBerr ;were there problems?
jnz send_err
clc
ret
send_err:
call count_out_err
stc
ret
send_pkt endp
public get_address
get_address:
;get the address of the interface.
;enter with es:di -> place to get the address, cx = size of address buffer.
;exit with nc, cx = actual size of address, or cy if buffer not big enough.
assume ds:code
cmp cx,EADDR_LEN
jnb get_addr_ok ;buffer ok
stc
ret
get_addr_ok:
push si
mov si,offset IBpadr
rep movsb
pop si
mov cl,EADDR_LEN
clc
ret
public set_address
set_address:
;enter with ds:si -> Ethernet address, CX = length of address.
;exit with nc if okay, or cy, dh=error if any errors.
assume ds:nothing
cmp cx,EADDR_LEN
jnb set_addr_ok ;buffer ok
mov dh,BAD_ADDRESS
stc
ret
set_addr_ok:
assume es:nothing
clc
ret
reset_mode proc near
; stop and restart the receiver
call reset_interface ;stop it
mov bx,c0_INIT ;re-init
call wrcsr0
mov cx,-1 ;wait until done
rm_lp:
call rdcsr0
test ax,c0_IDON ;ok?
jnz rm_IDON ;yes...
loop rm_lp ;else continue
rm_IDON:
mov bx,c0_STRT+c0_INEA+c0_IDON+c0_RXON+c0_TXON
call wrcsr0 ;start the receiver
ret
reset_mode endp
rcv_mode_1:
mov IBmode,M_DRX or M_DTX
jmp reset_mode
rcv_mode_3:
mov IBmode,0
jmp reset_mode
rcv_mode_6:
mov IBmode,M_PROM
jmp reset_mode
public set_multicast_list
set_multicast_list:
;enter with es:di ->list of multicast addresses, cx = number of bytes.
;return nc if we set all of them, or cy,dh=error if we didn't.
mov dh,NO_MULTICAST
stc
ret
public terminate
terminate:
call rcv_mode_1 ;don't receive any apckets.
;This routine will remove the (host) DMA controller from
;cascade mode of operation.
mov al,DMAPrt
or al,4
cmp DMAPrt,4 ;If channel 5 or 6,
ja terminate_16 ; use sixteen bit dma.
terminate_8:
out DMAC1MaskReg,al
jmp short terminate_done
terminate_16:
out DMAC2MaskReg,al
terminate_done:
ret
public get_multicast_list
get_multicast_list:
;return with nc, es:di ->list of multicast addresses, cx = number of bytes.
;return cy, NO_ERROR if we don't remember all of the addresses ourselves.
;return cy, NO_MULTICAST if we don't implement multicast.
mov dh,NO_MULTICAST
stc
ret
public reset_interface
reset_interface proc near
;reset the interface.
assume ds:code
mov bx,c0_STOP
call wrcsr0
ret
reset_interface endp
;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type.
extrn recv_find: near
;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
extrn recv_copy: near
public recv
recv proc near
;called from the recv isr. All registers have been saved, and ds=cs.
;Upon exit, the interrupt will be acknowledged.
assume ds:code
call rdcsr0 ;obtain interrupting status
and ax,0ffffh-c0_INEA ;disable interrupts
; mov RXStatus,Active ;Indicate receive is active
mov bx,ax
call wrcsr0 ;clear acknowledged i-sources
mov rcv_csr0,bx ;save
; test bx,c0_ERR ;any errors?
; jz rcv_noerr ;no...
; ... errors checked on descriptor level
rcv_noerr:
test bx,c0_RINT ;received a packet
jz rcv_norint
call do_rcv ;process...
rcv_norint:
rcv_ret:
mov bx,c0_INEA ;re-enable interrupts
cli
call wrcsr0
ret
recv endp
do_rcv proc near
mov bx,rbfcurr ;address descriptor
rcv_lp:
mov al,RBflags[bx]
test al,RBown ;something in buffer?
jz rcv_dobuf ;yes...
ret ;else return
rcv_dobuf:
test al,RBerr ;any errors?
jz rcv_good ;no...
test al,RBoflo ;overflow bit set?
jz rcv_err_1 ;no...
test al,RBenp ;ghost overflow error?
jnz rcv_good ;yes, ignore the error
rcv_err_1:
rcv_do_err:
; test al,RBoflo+RBbuff ;only count these
; jz rcv_next
call count_in_err
rcv_next:
call rcv_nxt_d ;next descriptor
jmp rcv_lp
rcv_good:
test al,RBstp ;packet starts here?
jz rcv_do_err ;no, unrolling error chain,
;count when error flag appears
rcv_good_stp:
; obtain packet length from last segment
rcv_L_lp:
test al,RBenp ;end of packet?
jnz rcv_L_ok ;yes, get length
add bx,8 ;to next descriptor
cmp bx,tbfstart ;overrun?
jb rcv_L_next ;no...
mov bx,rbfstart
rcv_L_next:
cmp bx,rbfcurr ;make sure we don't loop!
je rcv_L_ouch ;this is nonsense
mov al,RBflags[bx] ;get flags
test al,RBown+RBerr ;anything wrong with it?
jz rcv_L_lp ;no, go ahead
mov dl,al
and dl,RBenp+RBoflo
cmp dl,RBenp+RBoflo ;ghost overflow?
je rcv_L_ok ;yes, let it go through
rcv_L_ouch:
mov bx,rbfcurr ;restore current
jmp rcv_do_err ;give up
rcv_L_ok:
mov cx,RBmcnt[bx] ;message length
sub cx,4 ;strip FCS
mov bx,rbfcurr ;restore descriptor addr
cmp cx,GIANT ;reasonable size?
jg rcv_do_err ;rubbish!
jcxz rcv_next ;nothing to do...
mov pklen,cx ;save packet length
mov pklen_rem,cx ;remains to be rcvd
call rcv_getaddr
mov di,ax
mov es,dx
add di,EADDR_LEN*2 ;point to type field
mov dl, BLUEBOOK ;assume bluebook Ethernet.
mov ax, es:[di]
xchg ah, al
cmp ax, 1500
ja BlueBookPacket
inc di ;set di to 802.2 header
inc di
mov dl, IEEE8023
BlueBookPacket:
call recv_find ;want this packet?
mov ax,es
or ax,di
mov bx,rbfcurr ;restore in case...
jz rcv_next ;no buffer, give up
push es
push di
rcv_seg_lp:
; the packet's segments are copied into the client's buffer.
; The calculated length did not include the 4 byte fcs. Care must
; be taken not to copy the fcs, which may (in the worst case) be
; part of the last two segments!
test RBflags[bx],RBenp ;packet ends here?
jz rcv_seg_l_buf ;no, use buffer size
mov cx,pklen_rem ;remaining msg length
or cx,cx ;still data?
jle rcv_done ;no, just part of fcs
jmp short rcv_seg_l_ok
rcv_seg_l_buf:
mov cx,RBbcnt[bx] ;buffer size
neg cx ;it's in two's complement
sub pklen_rem,cx ;correct remaining len
jnl rcv_seg_l_ok ;use whole buffer
; the current buffer contains part of the fcs...
add cx,pklen_rem ;use only data part
rcv_seg_l_ok:
lds si,rcvbuffp ;point to buffer
assume ds:nothing
test cl,1 ;prepare for movsw
jz rcv_even
movsb ;copy first byte
rcv_even:
shr cx,1
jcxz rcv_moved
rep movsw ;copy the packet
rcv_moved:
push cs
pop ds
assume ds:code
test RBflags[bx],RBenp ;end of packet?
jnz rcv_done ;yes...
call rcv_nxt_d ;to next descriptor
call rcv_getaddr ;next buffer addr
jmp rcv_seg_lp
rcv_done:
mov cx,pklen
pop si ;restore buffer addr
pop ds
assume ds:nothing
call recv_copy ;wake up client
push cs
pop ds
assume ds:code
mov bx,rbfcurr ;current descriptor
jmp rcv_next ;else do next descriptor
do_rcv endp
public recv_exiting
recv_exiting:
;called from the recv isr after interrupts have been acknowledged.
;Only ds and ax have been saved.
assume ds:nothing
ret
; Most of the initialisation code is used only once and can be
; dicarded when init finishes, but not this:
; we formatted the buffer descriptors (a maximum space for
; that could be provided), but where could we place this code
; if buffers are allocated immediately afterwards?
; Too dangerous in the buffer area, since we will start the receiver now.
; Possible alternative: relocate to an address after the buffers, but
; since it's only a few bytes...
eto_go:
assume ds:code
call wrcsr ;start the Lance
pop dx ;get our ending paragraph
mov ah,31h
int 21h ;terminate, stay resident
; Provide maximum space for buffer descriptors before more
; discardable initialisation code starts, since the decriptors
; must be formatted. Buffers may follow immediately afterwards,
; but will not be used until the receiver is started.
; All resident code and data must be before this label!
; on quadword boundary...
public buffers_start
buffers_start db 0
org begin+( (($-begin+7)/8)*8 )
FirstDescr db ((128+128)*8) dup (0)
public usage_msg
usage_msg db "usage: ISOLINK [-n] [-d] [-w] <packet_int_no> <options>",CR,LF
db "options: [defaults]",CR,LF
db " /D<DMA channel #> [0]",CR,LF
db " /I<hw-int-level> [10]",CR,LF
db " /P<I/O port address> [(8)280]",CR,LF
db " /R<# recv buffs> <recv buff size> [16 256]",CR,LF
db " /T<# xmit buffs> <xmit buff size> [1 0]",CR,LF
db " /X (requires himem.sys installed)",CR,LF
db "$"
public copyright_msg
copyright_msg db "Packet driver for BICC 4110-2/3 ISOLAN controllers,"
db "version ",'0'+(majver / 10),'0'+(majver mod 10),".",'0'+version,CR,LF
db "Written by R.Toebbicke, CERN, Switzerland",CR,LF
db "* Copyright CERN, Geneva 1990 - Copyright and any other",CR,LF
db "* appropriate legal protection of these computer programs",CR,LF
db "* and associated documentation reserved in all countries",CR,LF
db "* of the world.",CR,LF,'$'
int_no_name db "Interrupt number $"
BaseName db "I/O Port address $"
BasePrt dw 08280h,0
DMAName db "DMA channel number $"
extrn our_isr: near, their_isr: dword
extrn packet_int_no: byte
extrn phd_environ: word
extrn decout: near
rcv_buffno dw 16,0
rcv_buffnoname db "Number of receive buffers $"
rcv_bufflog db 4
rcv_buffsz dw 256,0
rcv_buffszname db "Receive buffer size $"
xmt_buffno dw 1,0
xmt_buffnoname db "Number of transmit buffers $"
xmt_bufflog db 0
xmt_buffsz dw 0,0
xmt_buffszname db "Transmit buffer size $"
orgseg dw ?
linaddrL dw 0
linaddrH dw 0
; keep these two together!
HMAaddr dw 00010h
HMAaddrS dw 0FFFFh
XMSctl dd ?
PS2IO dw 08280h, 09250h, 0a390h, 0b1d0h
PS2INT db 9, 10, 11, 15, 3, 4, 5, 0
extrn set_recv_isr: near
;enter with si -> argument string, di -> word to store.
;if there is no number, don't change the number.
extrn get_number: near
;enter with dx -> name of word, di -> dword to print.
extrn print_number: near
extrn skip_blanks: near
argerr db "Error in options: '"
argerrc db " "
db "'.",CR,LF,"$"
parse_err db 0
public parse_args
parse_args proc near
; If we run on a PS/2, read the default configuration from the POS regs
test sys_features,MICROCHANNEL
jnz do_mc_defaults
jmp parse_args_l
do_mc_defaults:
mov bx,08h ;1st slot
pa_slot_l:
mov dx,96h
mov ax,bx
out dx,al ;put in setup mode
mov dx,101h
in al,dx
xchg al,ah ;high id byte
dec dx
in al,dx ;low id byte
cmp ax,0808h ;ISOLINK card?
je pa_found_iso ;yes...
inc bx ;next slot
cmp bx,0fh ;too far?
jna pa_slot_l ;no, try next
mov dx,96h
xor al,al
out dx,al ;exit setup mode
jmp parse_args_l
pa_found_iso:
mov dx,102h
in al,dx ;get I/O address code
xor ah,ah
and al,6
mov bx,ax
mov ax,PS2IO[bx] ;I/O port address
mov BasePrt,ax
mov dx,104h
in al,dx
xor ah,ah
shr al,5 ;isolate interrupt bits
mov bx,ax
mov al,PS2INT[bx] ;interrupt number
mov int_no,ax
parse_args_l:
call skip_blanks
cmp al,CR ;end of args?
je pa_ret ;yes...
cmp al,'/' ;option following?
je pa_doopt ;yes
pa_err:
mov argerrc,al
mov dx,offset argerr
pa_err2:
mov ah,9
int 21h ;print error message
mov parse_err,1 ;indicate error
pa_ret:
ret
pa_doopt:
inc si
lodsb ;get option byte
cmp al,'a' ;in lowercase range?
jna pa_upper
sub al,20h ;convert to uppercase
pa_upper:
cmp al,'I'
je pa_doInt
cmp al,'P'
je pa_doPort
cmp al,'D'
je pa_doDMA
cmp al,'R'
je pa_doRcv
cmp al,'T'
je pa_doTrans
cmp al,'X' ;into extended memory?
je pa_doext
jmp pa_err
pa_doInt:
mov di,offset int_no
call get_number
jmp parse_args_l
pa_doPort:
mov di,offset BasePrt
call get_number
jmp parse_args_l
pa_doDMA:
mov di,offset DMAPrt
call get_number
jmp parse_args_l
pa_doRcv:
mov di,offset rcv_buffno
call get_number
mov di,offset rcv_buffsz
call get_number
jmp parse_args_l
pa_doTrans:
mov di,offset xmt_buffno
call get_number
mov di,offset xmt_buffsz
call get_number
jmp parse_args_l
pa_doext: ;set up for HMA
or options,options_HMA ;indicate we want to run up there
jmp parse_args_l
parse_args endp
public print_parameters
print_parameters proc
mov di,offset int_no
mov dx,offset int_no_name
call print_number
mov di,offset BasePrt
mov dx,offset BaseName
call print_number
mov di,offset DMAPrt
mov dx,offset DMAName
call print_number
mov di,offset rcv_buffno
mov dx,offset rcv_buffnoname
call print_number
mov di,offset rcv_buffsz
mov dx,offset rcv_buffszname
call print_number
mov di,offset xmt_buffno
mov dx,offset xmt_buffnoname
call print_number
mov di,offset xmt_buffsz
mov dx,offset xmt_buffszname
call print_number
ret
print_parameters endp
inv_DMA db "Invalid DMA channel number.",CR,LF,"$"
init_errmsg db "Lance initialisation failed.",CR,LF,"$"
inv_buff db "Invalid buffer specification.",CR,LF,"$"
HMAnoHIMEM db "HMA not available: HIMEM.SYS not installed.",CR,LF,"$"
HMAnotavail db "HMA not available.",CR,LF,"$"
HMAalloc db "Using extended memory (HMA).",CR,LF,"$"
HMAtoobig db "Buffers exceed HMA size.",CR,LF,"$"
HMAmsg_1 db "/X configured $"
HMAmsg_2 db " receive buffers of length $"
HMAmsg_3 db ".",CR,LF,"$"
public etopen
etopen proc near
assume ds:code
extrn flagbyte: byte
or flagbyte,CALLED_ETOPEN
test parse_err,0ffh ;any parsing error?
jnz eto_errexit2 ;yes, give up
cmp packet_int_no,0
mov dx,offset usage_msg
jz eto_errexit
; check DMA port
mov al,DMAPrt
or al,al
jb eto_invalid_DMA ;should not be negative
cmp al,7
jna eto_DMA_ok
eto_invalid_DMA:
mov dx,offset inv_DMA
eto_errexit:
mov ah,9
int 21h
eto_errexit2:
stc
ret
eto_DMA_ok:
; check receive buffers
mov bx,rcv_buffno
call do_power_of_2
cmp ax,128
jg eto_inv_Buff
or ax,ax ;must have something!
jz eto_inv_Buff
mov rcv_bufflog,cl
mov rcv_buffno,ax
mov ax,rcv_buffsz ;check buffer size
cmp ax,100 ;at least that big
jg eto_rcvsz_1
mov ax,100
eto_rcvsz_1:
cmp ax,GIANT+4 ;biggest packet plus fcs
jl eto_rcvsz_2 ;does not exceed
mov ax,GIANT+4 ;take maximum
eto_rcvsz_2:
mov rcv_buffsz,ax
; check transmit buffers
mov bx,xmt_buffno
call do_power_of_2
or ax,ax ;must have something!
jz eto_inv_Buff
cmp ax,128
jna eto_xmt_buffok
eto_inv_Buff:
mov dx,offset inv_Buff
jmp eto_errexit
eto_xmt_buffok:
mov xmt_bufflog,cl
mov xmt_buffno,ax
mov ax,xmt_buffsz ;check buffer size
cmp ax,RUNT ;at least that big
jg eto_xmtsz_1
mov ax,0 ;else don't use
eto_xmtsz_1:
cmp ax,GIANT ;biggest packet w/o fcs
jl eto_xmtsz_2 ;does not exceed
mov ax,GIANT ;take maximum
eto_xmtsz_2:
mov xmt_buffsz,ax
mov xmt_buffsz_r,ax ;again for our resident part
neg ax
mov xmt_buffsz_rn,ax ;times -1 for resident part
; set RAP & RDP addresses
mov ax,BasePrt
add ax,0ch
mov RDP,ax
add ax,02h
mov RAP,ax
; this code is from Russell Nelson's packet driver skeleton.
; Need it here, since we won't return from etopen: as soon as the
; receiver is started, this place may get clobbered with Ethernet packets
mov ah,35h ;remember their packet interrupt.
mov al,packet_int_no
int 21h
mov their_isr.offs,bx
mov their_isr.segm,es
mov ah,25h ;install our packet interrupt
mov dx,offset our_isr
int 21h
call set_recv_isr ;intercept interrupts
; Now we decide whether we will run in the HMA (above 1 MB) before
; we do any calculations involving real addresses
test options,options_HMA ;will we?
jnz eto_tryHMA ;yes
jmp eto_HMAok ;no...
; first check of HIMEM.SYS is installed and if we can globally
; enable the HMA
eto_tryHMA:
mov ax,4300h
int 2Fh ; Is an XMS Driver installed?
mov dx,offset HMAnoHIMEM ;error message in case...
cmp al,80h
jne eto_noHMA ;no...
mov ax,4310h
int 2fh ;obtain XMS control addr
mov word ptr XMSctl,bx ;save entry point
mov word ptr XMSctl+2,es
mov dx,0ffffh ;allocate whole HMA
mov ah,1
call XMSctl
mov dx,offset HMAnotavail ;error msg in case...
cmp ax,1
jne eto_noHMA ;not available
mov ah,3 ;global enable A20 line
call XMSctl
cmp ax,1 ;ok?
je eto_HMA_enabled
eto_noHMA:
mov ah,9
int 21h ;error msg addr in dx!
and options,not options_HMA ;turn off HMA bit
jmp eto_HMAok ;continue as usual
eto_HMA_enabled:
; mov dx,offset HMAalloc
; mov ah,9
; int 21h ;confirm
; maximize the receive buffer space to utilise the whole HMA:
; 1. calculate remaining space excluding receive buffers and descriptors
; 2. start with 128 receive buffers
; 3. calculate remaining space for buffers
; 4. divide by number of buffers to obtain (even) buffer size
; 5. if size inferior to defaulted or specified size half number
; of buffers and restart on step 2.
mov bx,0fff0h ;total HMA size
sub bx,offset FirstDescr ;minus resident code
mov ax,xmt_buffsz ;xmit buffer size
add ax,8 ;plus descriptor
mul xmt_buffno ;=total xmit space
or dx,dx ;not more than 64k!
jnz try_err ;ooops
sub bx,ax ;remaining space without recv
jc try_err ;must be positive!
mov cx,128 ;try 128 buffers
try_lp:
mov ax,cx ;number of buffers
shl ax,3 ;8 bytes long
sub ax,bx
neg ax ;remaining space for buffers
xor dx,dx ;accumulator extension
div cx ;calculate buffer length
and ax,0fffeh ;even length
cmp ax,rcv_buffsz ;long enough?
jnb try_ok ;yes...
shr cx,1 ;else half number of buffers
jnz try_lp ;and retry
try_err:
mov dx,offset HMAtoobig
jmp eto_errexit
try_ok:
cmp ax,GIANT+4 ;maximum size
jng try_ok_2 ;does not exceed
mov ax,GIANT+4 ;else maximum length
try_ok_2:
mov rcv_buffno,cx
mov rcv_buffsz,ax
mov bx,cx
call do_power_of_2 ;have to do it again
mov rcv_bufflog,cl
mov dx,offset HMAmsg_1
mov ah,9
int 21h
mov ax,rcv_buffno
xor dx,dx
call decout
mov dx,offset HMAmsg_2
mov ah,9
int 21h
mov ax,rcv_buffsz
xor dx,dx
call decout
mov dx,offset HMAmsg_3
mov ah,9
int 21h
; la demarche a suivre:
; we copy ourselves into the HMA completely, format all the
; descriptors, correct the interrupt addr and exit to DOS
mov ax,cs
mov orgseg,ax ;save original segment
les di,dword ptr HMAaddr ;that's where we will be
mov cx,offset end_code ;length(!) of code to be copied
mov si,10h ;start at offset 10
sub cx,si ;correct length
rep movsb ;copy us
push HMAaddrS ;our new segment
; careful: here comes a jump!
call setcs ;we're in the HMA
push cs
pop ds ;our data as well
eto_HMAok:
; get linear address for receive buffer descriptor start
push ds
mov ax,offset FirstDescr
pop dx
call seg2lin
; Store in Init Block
mov IBrdraL,ax
mov IBrdraH,dx
; calculate linear address of first buffer
mov bx,offset FirstDescr
mov rbfstart,bx
mov rbfcurr,bx
mov ax,rcv_buffno
push ds
pop dx
add ax,xmt_buffno ;result is still in segment
shl ax,3 ;times eight
add ax,bx ;won't leave segment!
call seg2lin ;convert to linear addr
mov linaddrL,ax ;first available buffer addr
mov linaddrH,dx
; format receive buffer descriptors
mov si,rcv_buffsz ;buffer size
mov cx,rcv_buffno ;number of buffers
mov dl,RBown ;flags
eto_rcvloop:
call format_descr
add bx,8
loop eto_rcvloop
;set transmit descriptor ring address
push ds
mov ax,bx
pop dx
call seg2lin
mov IBtdraL,ax
mov IBtdraH,dx
; format transmit buffer descriptors
mov tbfstart,bx ;remember start
mov tbfcurr,bx
mov dl,0 ;initial flags
mov cx,xmt_buffno ;number of buffers
mov si,xmt_buffsz ;buffer size
eto_xmtloop:
call format_descr
add bx,8
loop eto_xmtloop
mov tbfend,bx ;remember end
; finish initialisation block
mov al,rcv_bufflog ;# of rcv buffers
shl al,5
mov IBrdraHF,al
mov al,xmt_bufflog ;# of xmt buffers
shl al,5
mov IBtdraHF,al
; copy Ethernet address out of ROM
mov cx,EADDR_LEN
push ds
pop es
mov di,offset IBpadr
mov dx,BasePrt
eto_addrloop:
in ax,dx
inc dx
inc dx
stosb
loop eto_addrloop
; init DMA controller
test sys_features,MICROCHANNEL ;running on a PS/2?
jnz eto_DMA_done ;yes, no DMA
cmp DMAPrt, 3 ; Which DMAC - 1 or 2 ?
ja SetupDMAC2 ; DMAC 2
mov dx,DMAC1ModeReg ;addr of mode reg 1
mov al,DMAPrt ;DMA number
or al,DMACascade ;or in mode reg bits
out dx,al
jmp $+2
and al,3 ;leave only DMA channel #
mov dx,DMAC1MaskReg ;mask register 1 addr
out dx,al ;clear the DMA channel
jmp eto_DMA_done
SetupDMAC2:
mov dx,DMAC2ModeReg ;addr of mode reg 2
mov al,DMAPrt ;DMA number
sub al, 4 ;Adjust DMA channel number
or al,DMACascade ;or in mode reg bits
out dx,al
jmp $+2
and al,3 ;leave only DMA channel #
mov dx,DMAC2MaskReg ;mask register 2 addr
out dx,al ;clear the DMA channel
jmp eto_DMA_done
eto_DMA_done:
; stop the Lance, sets required bits in CSR3 as well
call reset_interface
; Give the LANCE the address of the initialisation block
call LoadInitBlock
; init the controller and wait for completion
call InitLance
eto_init_ok:
test options,options_HMA ;are we in HMA?
jz eto_tsr ;no, use TSR code
xor ax,ax
; correct the packet and hardware interrupt addresses
mov es,ax
xor bh,bh
mov bl,packet_int_no
shl bx,2 ;point to interrupt vector
push cs
pop es:[bx+2]
mov bx,int_no ;card interrupt
add bx,68h ;assume above 8
cmp bx,8+68h ;is it?
jnb eto_HMA_int_ok ;yes...
sub bx,60h ;PS/2 can use interrupt < 8
eto_HMA_int_ok:
shl bx,2 ;point to interrupt vector
push cs
pop es:[bx+2]
; Now start the receiver and exit to DOS
call outofHMA ;out of danger zone (buffers!)
mov bx,c0_STRT+c0_INEA+c0_IDON+c0_RXON+c0_TXON ; ...SET...
call wrcsr0 ; ...GO!
int 20h ;exit...
eto_tsr:
;calculate end of resident part (relative to pgm start)
mov ax,linaddrL
mov dx,linaddrH
add ax,15 ;round to paragraph
adc dl,0
shr ax,4
shl dl,4
or ah,dl
mov dx,ds ;minus pgm start addr
sub ax,dx
push ax ;save the address
; again some code from the packet driver skeleton...
mov ah,49h ;free our environment, because
mov es,phd_environ ; we won't need it.
int 21h
mov bx,1 ;get the stdout handle.
mov ah,3eh ;close it in case they redirected it.
int 21h
; call setup_isr ;Install LANCE lockup isr
mov ax,csr0 ; READY...
mov bx,c0_STRT+c0_INEA+c0_IDON+c0_RXON+c0_TXON ; ...SET...
jmp eto_go ; ...GO!
etopen endp
do_power_of_2 proc near
; adjust bx to valid # of buffers.
; on exit: ax=fitting number, cx=log2(ax)
mov cx,7 ;allow 7 shifts
mov ax,bx
dpw2_loop:
cmp ax,1
je dpw2_done
shr ax,1
loop dpw2_loop
dpw2_done:
mov ax,7
sub ax,cx ;get number of shifts
mov cx,ax
mov ax,1
jcxz dpw2_ret
shl ax,cl
dpw2_ret:
ret
do_power_of_2 endp
format_descr proc near
; format buffer descriptor pointed to by [bx]
; si=buffer size, dl=flags
push dx ;save flags
mov ax,linaddrL ;next available buffer addr
mov dx,linaddrH
mov RBadrL[bx],ax ;buffer addr
mov RBadrH[bx],dx
add ax,si ;point to next available addr
adc dx,0
mov linaddrL,ax
mov linaddrH,dx
mov ax,si ;get buffer size
; or ah,0f0h ;reserved bits to 1
neg ax ;two's complement
mov RBbcnt[bx],ax
pop dx ;restore flags
mov RBflags[bx],dl
ret
format_descr endp
outofHMA proc near ;leave the HMA
test options,options_HMA ;are we in it?
jz ooHret ;no...
push orgseg
call setcs
push cs
pop ds
ooHret:
ret
outofHMA endp
setcs proc near
db 0cbh ;RET FAR (!)
setcs endp
disp_hex_num PROC NEAR
push ax
push bx
push cx
mov bx,ax
mov cx,4
start:
push cx
mov cl,4
rol bx,cl
push bx
and bl,0fh
mov al,bl
cmp al,09
jg letter
digit:
add al,48
jmp print_num
letter:
add al,55
print_num:
mov ah,14
int 10h
pop bx
pop cx
loop start
mov al,20h
mov ah,14
int 10h
pop cx
pop bx
pop ax
ret
; mov ax,4c00h ; Load DOS exit function
; int 21h ; call DOS
disp_hex_num ENDP
end_code equ $
PUBLIC mc_end
mc_end db 0
code ends
end